home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / winsock / winftp.zip / WNFTPSRC.ZIP / WS_CON.C < prev    next >
C/C++ Source or Header  |  1994-01-10  |  41KB  |  1,266 lines

  1. #include "ws_glob.h"
  2. #include "winftp.h"
  3. #include <stdio.h>
  4. #include <stdarg.h>
  5. #include <stdlib.h>
  6. #include <ctype.h>
  7. #include <io.h>
  8. #include <fcntl.h>
  9. #include <sys\stat.h>
  10. #include <time.h>
  11.  
  12. // extern int errno;
  13.  
  14. extern BOOL bAborted;   // timer routine may set this
  15. extern BOOL bDebugLog;  
  16.  
  17. char szDbgLogFile[_MAX_PATH];
  18.  
  19. //***********************************************************************
  20. //***********************************************************************
  21. int WriteDebugLog (int nCode, int nRetCode, LPSTR lpStr)
  22. {
  23.   FILE *fp;
  24.   FARPROC lpfnMsgProc;
  25.   int nRC;
  26.   
  27.   if (!bDebugLog) return 0;
  28.   if (lstrlen (szDbgLogFile)==0)
  29.   {
  30.     lstrcpy (szDlgPrompt,"Enter log file name:");
  31.     lstrcpy (szDlgEdit, "C:\\WNFTPDBG.LOG");
  32.     lpfnMsgProc = MakeProcInstance ((FARPROC) WS_InputMsgProc, hInst);
  33.     nRC = DialogBox (hInst, (LPSTR) "DLG_INPUT", hWndMain, lpfnMsgProc);
  34.     FreeProcInstance (lpfnMsgProc);
  35.     if (nRC==IDCANCEL) return 0;
  36.     lstrcpy (szDbgLogFile, szDlgEdit);
  37.   }
  38.   fp = fopen (szDbgLogFile, "at");
  39.   switch (nCode)
  40.   {
  41.     case 0: fprintf (fp, "SEND: %s\n", lpStr); break;
  42.     case 1: fprintf (fp, "RESULT: %4d  %3d  %-16s  %s\n", nRetCode, iCode,
  43.                            (nRetCode==FTP_PRELIM)   ? "Prelim" :
  44.                            (nRetCode==FTP_COMPLETE) ? "Complete" : 
  45.                            (nRetCode==FTP_CONTINUE) ? "Continue" : 
  46.                            (nRetCode==FTP_RETRY)    ? "Retry" : 
  47.                            (nRetCode==FTP_ERROR)    ? "Error" : "Unknown", 
  48.                            lpStr); break;
  49.   }
  50.   fclose (fp);
  51.   return 0;
  52. }
  53.  
  54. //***********************************************************************
  55. //  Print statistics on the transfer
  56. //***********************************************************************
  57. void PrintTransferStatus (LPSTR lpTyp, LONG lBytes, long nSecs)
  58. {
  59.   LONG lSecs = nSecs;
  60.   
  61.   if (lSecs==0) lSecs=1;
  62.   DoPrintf ("%sed %ld characters in %ld seconds (%ld bytes/sec)", lpTyp,
  63.         lBytes, (long) nSecs, (long) lBytes/lSecs);
  64. }
  65.  
  66. static UINT nDirNum=0;
  67.  
  68. //***********************************************************************
  69. //  Send in a Command line and return a code indicating type of reply
  70. // send a message on the control socket, read and display the resulting
  71. // message and return the result value
  72. //***********************************************************************
  73. int getreply (SOCKET ctrl_skt,LPSTR cmdstring, BOOL bForce)
  74. {
  75.   int iRetCode=0;
  76.  
  77.   iCode=0;
  78.   if (strncmp (cmdstring,"PASS ",5)==0) DoAddLine ("PASS xxxxxx");
  79.   else DoAddLine (cmdstring);
  80.   if (ctrl_skt==INVALID_SOCKET)
  81.   {
  82.     DoAddLine ("Not connected");
  83.   }
  84.   else 
  85.   {
  86.     if (bDebugLog) WriteDebugLog (0, iRetCode, cmdstring);
  87.     switch (bForce)
  88.     {
  89.       case TRUE : if (ForcePacket (ctrl_skt,cmdstring)!=-1) iRetCode = ReadDisplayLine (ctrl_skt);
  90.       case FALSE: if (SendPacket (ctrl_skt,cmdstring)!=-1) iRetCode = ReadDisplayLine (ctrl_skt);
  91.     }
  92.     if (bDebugLog) WriteDebugLog (1, iRetCode, cmdstring);
  93.   }
  94.   return iRetCode;  // 0 - 5
  95. }
  96.  
  97. //***********************************************************************
  98. //  Send in a Command line and return a code indicating type of reply
  99. //***********************************************************************
  100. int command (SOCKET ctrl_skt, char *fmt,...)
  101. {
  102.   va_list args;
  103.   char szBuf[90];
  104.  
  105.   va_start (args, fmt);
  106.   vsprintf (szBuf, fmt, args);
  107.   va_end (args);
  108.   return getreply (ctrl_skt, szBuf, FALSE);
  109. }
  110.  
  111. //***********************************************************************
  112. //  Send in a Command line and return a code indicating type of reply
  113. //***********************************************************************
  114. int ForceCommand (SOCKET ctrl_skt, char *fmt,...)
  115. {
  116.   va_list args;
  117.   char szBuf[90];
  118.  
  119.   va_start (args, fmt);
  120.   vsprintf (szBuf, fmt, args);
  121.   va_end (args);
  122.   return getreply (ctrl_skt, szBuf, TRUE);
  123. }
  124.  
  125. //***********************************************************************
  126. // return a string pointer to ON or OFF based on the flag
  127. //***********************************************************************
  128. char *onoff(BOOL flag)
  129. {
  130.   if (flag) return("ON"); else return("OFF");
  131. }
  132.  
  133. //***********************************************************************
  134. // process CWD
  135. //***********************************************************************
  136. int DoCWD(SOCKET ctrl_skt,LPSTR path)
  137. {
  138.   char szPath[100];
  139.   
  140.   if (command (ctrl_skt,"CWD %s",path)==FTP_ERROR)
  141.   {
  142.     if (iCode==500) command (ctrl_skt,"XCWD %s",path);
  143.     else
  144.     {
  145.       lstrcpy (szPath, path);
  146.       ConvertTargetDir (szPath, 95);
  147.       command (ctrl_skt,"CWD %s", szPath);
  148.     }
  149.   }
  150.   return(iCode/100);
  151. }
  152.  
  153. //***********************************************************************
  154. // process System Type
  155. //***********************************************************************
  156. int DoSystemCommand (SOCKET ctrl_skt)
  157. {
  158.   LPSTR lp;
  159.   char szBuf[100];
  160.   
  161.   if (command (ctrl_skt,"SYST")!=FTP_ERROR)
  162.   {
  163.     lstrcpy (szBuf, szMsgBuf);
  164.     strupr (szBuf);
  165.     if (strstr (szBuf, "UNIX") != NULL)         nHostType = HOST_UNIX;
  166.     else if (strstr (szBuf, "ULTRIX") != NULL)  nHostType = HOST_UNIX;
  167.     else if (strstr (szBuf, "MVS") != NULL)     nHostType = HOST_MVS;
  168.     else if (strstr (szBuf, "QVT") != NULL)     nHostType = HOST_QVT;
  169.     else if (strstr (szBuf, "NCSA") != NULL)    nHostType = HOST_NCSA;
  170.     else if (strstr (szBuf, "CHAMELEON")!=NULL) nHostType = HOST_CHAMELEON;
  171.     else if (strstr (szMsgBuf, "VMS") != NULL)
  172.     {
  173.       lp = strstr (szMsgBuf, "MultiNet");
  174.       nHostType = (lp!=NULL)? HOST_VMS_MULTINET : HOST_VMS_UCX;
  175.     }
  176.   }
  177.   return(iCode/100);
  178. }
  179.  
  180. //***********************************************************************
  181. // proces PWD
  182. //***********************************************************************
  183. int DoPWD(SOCKET ctrl_skt)
  184. {
  185.   if (command(ctrl_skt,"PWD")==FTP_ERROR && iCode==500) 
  186.   {
  187.     command(ctrl_skt,"XPWD");
  188.   }
  189.   return(iCode/100);
  190. }
  191.  
  192. //***********************************************************************
  193. // process MKD
  194. //***********************************************************************
  195. int DoMKD(SOCKET ctrl_skt,LPSTR pathname)
  196. {
  197.   char szPath[100];
  198.   
  199.   if (command (ctrl_skt,"MKD %s",pathname)==FTP_ERROR)
  200.   {
  201.     if (iCode==500) command (ctrl_skt,"XMKD %s",pathname);
  202.     else
  203.     {
  204.       lstrcpy (szPath, pathname);
  205.       ConvertTargetDir (szPath, 95);
  206.       command (ctrl_skt,"MKD %s", szPath);
  207.     }
  208.   }
  209.   return(iCode/100);
  210. }
  211.  
  212. //***********************************************************************
  213. // process RMD
  214. //***********************************************************************
  215. int DoRMD(SOCKET ctrl_skt,LPSTR pathname)
  216. {
  217.   char szPath[100];
  218.   
  219.   if (command (ctrl_skt,"RMD %s",pathname)==FTP_ERROR)
  220.   {
  221.     if (iCode==500) command (ctrl_skt,"XRMD %s",pathname);
  222.     else
  223.     {
  224.       lstrcpy (szPath, pathname);
  225.       ConvertTargetDir (szPath, 95);
  226.       command (ctrl_skt,"RMD %s", szPath);
  227.     }
  228.   }
  229.   return(iCode/100);
  230. }
  231.  
  232. //***********************************************************************
  233. // process DELE
  234. //***********************************************************************
  235. int DoDELE(SOCKET ctrl_skt,LPSTR pathname)
  236. {
  237.   command(ctrl_skt,"DELE %s",pathname);
  238.   return(iCode/100);
  239. }
  240.  
  241. //***********************************************************************
  242. //  Send the Quit Command
  243. //***********************************************************************
  244. int DoDisconnect (SOCKET ctrl_skt)
  245. {
  246.   int nRC=-1;
  247.  
  248.   if (ctrl_skt!=INVALID_SOCKET)
  249.   {
  250.     nRC=command (ctrl_skt, "quit");
  251.     shutdown (ctrl_skt, 2);
  252.   }
  253.   return nRC;
  254. }
  255.  
  256. //***********************************************************************
  257. // process user command
  258. //***********************************************************************
  259. int DoQUOTE(SOCKET ctrl_skt,LPSTR string)
  260. {
  261.   if(strncmp(string,"LIST",4)==0 ||
  262.      strncmp(string,"NLST",4)==0)
  263.     DoDirList(ctrl_skt,string);
  264.   else
  265.     command(ctrl_skt,string);
  266.   return(iCode/100);
  267. }
  268.  
  269. //***********************************************************************
  270. // process chmod
  271. //***********************************************************************
  272. int DoCHMOD(SOCKET ctrl_skt,LPSTR modes,LPSTR filename)
  273. {
  274.   return (command(ctrl_skt,"SITE CHMOD %s %s",modes,filename));
  275. }
  276.  
  277. extern BOOL bHELP,bIs5000;
  278. LPSTR szAcctAttempt="Attempting to send Account Password...";
  279.  
  280. //***********************************************************************
  281. // initial connection
  282. //***********************************************************************
  283. SOCKET DoConnect (LPSTR lpHost)
  284. {
  285.   int iLength,iRetCode;
  286.   int iFlag=1;
  287.   SOCKET ctrl_skt;
  288.   LPSTR lpSite;
  289.  
  290.   if (bConnected) 
  291.   {
  292.     DoAddLine("Already connected!");
  293.     return (INVALID_SOCKET);
  294.   }
  295.  
  296.   //***************************************************************
  297.   // if connecting through a Firewall Host, trick the connect code
  298.   // into connecting to the FireWall Host instead.
  299.   //***************************************************************
  300.   if (bFireWall) 
  301.   {
  302.     if (lstrlen (szFireWallHost)==0)
  303.     {
  304.       DoPrintf ("FireWall Host name not specified. Can not Connect.");
  305.       SetDebugWindowText (lpDebugWindow);
  306.       return INVALID_SOCKET;
  307.     }
  308.     lpHost = szFireWallHost;
  309.   }
  310.  
  311.   bHELP=bIs5000=FALSE;
  312.   SetDebugWindowText ("WINFTP Connecting...");
  313.   lpSite = lpHost;
  314.  
  315.   //***************************************************************
  316.   // let other routines know that we are busy
  317.   //***************************************************************
  318.   bCmdInProgress++;
  319.  
  320.   //***************************************************************
  321.   // create a connected socket
  322.   //***************************************************************
  323.   if ((ctrl_skt=connectTCP (lpHost,"ftp"))==INVALID_SOCKET) 
  324.   {
  325.     DoPrintf ("Connection to %s failed", lpHost);
  326.     bCmdInProgress--;
  327.     SetDebugWindowText (lpDebugWindow);
  328.     return INVALID_SOCKET;
  329.   }
  330.  
  331.   //***************************************************************
  332.   // get information about local end of the connection
  333.   //***************************************************************
  334.   iLength = sizeof (saCtrlAddr);
  335.   if (getsockname (ctrl_skt, (struct sockaddr *) &saCtrlAddr, &iLength)==SOCKET_ERROR)
  336.   {
  337.     ReportWSError ("getsockname", WSAGetLastError());
  338.     bCmdInProgress--;
  339.     DoClose (ctrl_skt);
  340.     SetDebugWindowText (lpDebugWindow);
  341.     return (INVALID_SOCKET);
  342.   }
  343.  
  344.   //***************************************************************
  345.   // show remote end address
  346.   //***************************************************************
  347.   DoPrintf ("[%u] from %s port %u", ctrl_skt, inet_ntoa (saCtrlAddr.sin_addr), ntohs (saCtrlAddr.sin_port));
  348.  
  349.   //***************************************************************
  350.   // get initial message from remote end
  351.   //***************************************************************
  352.   while ((iRetCode=ReadDisplayLine(ctrl_skt))==FTP_PRELIM)
  353.   {
  354.     if (strstr(szMsgBuf,"(EXOS")!=NULL) bIs5000=TRUE;
  355.   }
  356.   
  357.   //***************************************************************
  358.   // if it succeeded
  359.   //***************************************************************
  360.   if (iRetCode!=FTP_COMPLETE) 
  361.   {
  362.     DoPrintf ("Unknown open msg \"%s\" %u",szMsgBuf,iCode);
  363.     // allow other processes to work
  364.     bCmdInProgress--;
  365.     DoClose ((SOCKET) ctrl_skt);
  366.     SetDebugWindowText (lpDebugWindow);
  367.     return (INVALID_SOCKET);
  368.   }
  369.   if (setsockopt (ctrl_skt, SOL_SOCKET, SO_OOBINLINE, (LPSTR) &iFlag, sizeof (iFlag))==SOCKET_ERROR)
  370.   {
  371.     ReportWSError("setsockopt",WSAGetLastError());
  372.   }
  373.  
  374.   //***************************************************************
  375.   // have to reset this so "command" will work
  376.   //***************************************************************
  377.   bCmdInProgress--;
  378.     
  379.   if (bFireWall)
  380.   {
  381.     if ((iRetCode=command(ctrl_skt,"USER %s", szFireWallUserID))==FTP_CONTINUE)
  382.     {
  383.       iRetCode = command (ctrl_skt, "PASS %s", szFireWallUserPass);
  384.     }
  385.     
  386.     if (iRetCode!=FTP_COMPLETE)
  387.     {
  388.       DoPrintf ("Failed to log in to Firewall Host %s", lpHost);
  389.       DoClose ((SOCKET) ctrl_skt);
  390.       return INVALID_SOCKET;
  391.     }
  392.       
  393.     //***************************************************************
  394.     //Okay, gets here if logged in to FireWall Host
  395.     //***************************************************************
  396.     wsprintf (szString, "site %s", lpSite);
  397.     if ((iRetCode=command (ctrl_skt, szString))==FTP_ERROR)
  398.     {
  399.       DoPrintf ("Could not connect to %s through FireWall Host", lpSite);
  400.       DoClose ((SOCKET) ctrl_skt);
  401.       bConnected=0;
  402.       return INVALID_SOCKET;
  403.     }
  404.  
  405.     //***************************************************************
  406.     // if here, we did get through to remote host, so do regular login
  407.     //***************************************************************
  408.     bConnected=1;
  409.   }
  410.     
  411.   //***************************************************************
  412.   // send our userid
  413.   //***************************************************************
  414.   SetDebugWindowText ("WINFTP UserName");
  415.   if ((iRetCode=command (ctrl_skt, "USER %s", szUserID))==FTP_CONTINUE)
  416.   {
  417.     //***************************************************************
  418.     // if the remote system requires a password, send it.
  419.     //***************************************************************
  420.     SetDebugWindowText ("WINFTP Password");
  421.     iRetCode = command (ctrl_skt,"PASS %s",szPassWord);
  422.     //***************************************************************
  423.     //  If the system requires an account password
  424.     //***************************************************************
  425.     if (bAccount)
  426.     {
  427.       nAcctType = 3;
  428.       switch (nAcctType)
  429.       {
  430.         case 0: break;
  431.         case 1: if (iRetCode!=FTP_CONTINUE) break;
  432.         case 2: DoAddLine (szAcctAttempt);
  433.                 if ((iRetCode=command (ctrl_skt, "ACCOUNT"))==FTP_CONTINUE)
  434.                 {
  435.                   iRetCode = command (ctrl_skt, szAccountPass);
  436.                 } break;
  437.         case 3: if (iRetCode!=FTP_CONTINUE) break;
  438.         case 4: DoAddLine (szAcctAttempt);
  439.                 wsprintf (szString, "ACCT %s", szAccountPass);
  440.                 iRetCode=command (ctrl_skt, szString);
  441.                 break;
  442.       }
  443.     }
  444.   }
  445.  
  446.   //***************************************************************
  447.   // if we are successfully logged on,.....
  448.   //***************************************************************
  449.   if (iRetCode!=FTP_COMPLETE)
  450.   {
  451.     DoPrintf ("Could not log on to remote host %s", lpSite);
  452.     DoClose ((SOCKET) ctrl_skt);
  453.     SetDebugWindowText (lpDebugWindow);
  454.     MessageBox (hWndMain, szMsgBuf+4, "Login Failed", MB_OK);
  455.     return INVALID_SOCKET;
  456.   }
  457.   
  458.   bConnected=1;
  459.   wsprintf (szString,"WINFTP: %s",szRemoteHost);
  460.   SetWindowText (hWndMain, szString);
  461.   SetDebugWindowText (lpDebugWindow);
  462.   return ctrl_skt;
  463. }
  464.  
  465. //***********************************************************************
  466. //***********************************************************************
  467. int DoDirList (SOCKET ctrl_skt,LPSTR szCMD)
  468. {
  469.   int nRC,nBell;
  470.   
  471.   nBell=bBell; bBell=0;
  472.   if (lstrlen (szCurrentDir)>0) unlink (szCurrentDir);
  473.   wsprintf (szCurrentDir, szTmpDirFile, nDirNum++);
  474.   nRC = RetrieveFile (ctrl_skt, szCMD, szCurrentDir, TYPE_A);
  475.   bBell = nBell;
  476.   return nRC;
  477. }
  478.  
  479. //***********************************************************************
  480. //***********************************************************************
  481. int SendFile(SOCKET ctrl_skt,LPSTR szCMD,LPSTR localfile,char stype)
  482. {
  483.   int iRetCode;
  484.   int iLength;
  485.  
  486.   iCode=0;
  487.   // if we don't have a valid control socket, can't do anything
  488.   if(ctrl_skt==INVALID_SOCKET) 
  489.   {
  490.     DoAddLine("no ctrl_skt, ignored");
  491.     return(0);
  492.   }
  493.   // if we are doing something, don't try to do this
  494.   if (bCmdInProgress) 
  495.   {
  496.     DoAddLine("command in process, ignored");
  497.     return(0);
  498.   }
  499.   
  500.   // if the requested type is not the same as the default type
  501.   if (cType!=stype) 
  502.   {
  503.     if(stype==TYPE_L)
  504.       command(ctrl_skt,"TYPE L 8");
  505.     else
  506.       command(ctrl_skt,"TYPE %c",stype);
  507.     cType=stype;
  508.   }
  509.   
  510.   // create a listener socket, if it is successful
  511.   if ((listen_socket=GetFTPListenSocket (ctrl_skt))!=INVALID_SOCKET) 
  512.   {
  513.     // send command to see the result of this all
  514.     // read the control channel (should return 1xx if it worked)
  515.     iRetCode = command ((SOCKET) ctrl_skt, szCMD);
  516.     if (iRetCode==FTP_PRELIM) 
  517.     {
  518.       // wait for connection back to us on the listen socket
  519.       nTimerID = SetTimer (hWndMain, 10, uiTimeOut, NULL);
  520.       // get our data connection
  521.       iLength=sizeof (saSockAddr1);
  522.       data_socket = accept (listen_socket, (struct sockaddr far *)&saSockAddr1, (int far *)&iLength);
  523.       // turn off the timeout timer
  524.       KillTimer (hWndMain, 10);
  525.       nTimerID = -1;
  526.       // if it failed, we have to quit this
  527.       if (data_socket==INVALID_SOCKET) 
  528.       {
  529.         ReportWSError("accept",WSAGetLastError());
  530.         listen_socket = DoClose (listen_socket);
  531.         iRetCode = FTP_ERROR;
  532.       } 
  533.       else 
  534.       {
  535.         // we don't need the listener socket anymore
  536.         // inform user of the connection
  537.         listen_socket = DoClose (listen_socket);
  538.         DoPrintf ("[%u] accept from %s port %u", data_socket,
  539.            inet_ntoa (saSockAddr1.sin_addr), ntohs (saSockAddr1.sin_port));
  540.  
  541.         // copy the file and close the socket
  542.         iRetCode = SendMass (data_socket, localfile, stype==TYPE_I);
  543.         data_socket = DoClose (data_socket);
  544.  
  545.         // read the close control message (should return 2xx)
  546.         switch (iRetCode)
  547.         {
  548.           case FTP_ABORT: break; //ForcePacket (ctrl_skt, "ABOR"); break;
  549.           default       : iRetCode = ReadDisplayLine (ctrl_skt);
  550.         }
  551.       }
  552.     } 
  553.     else 
  554.     {
  555.       listen_socket = DoClose (listen_socket);
  556.       iRetCode=0;
  557.       if (bBell) MessageBeep (MB_ICONEXCLAMATION);
  558.     }
  559.   } 
  560.   else 
  561.   {
  562.     listen_socket = DoClose (listen_socket);
  563.     iRetCode=FTP_ERROR;
  564.     if (bBell) MessageBeep (MB_ICONEXCLAMATION);
  565.   }
  566.   return iRetCode;
  567. }
  568.  
  569. //*****************************************************************************
  570. //*****************************************************************************
  571. void ExtractFileSize()
  572. {
  573.   char szBuf[100];
  574.   LONG nSiz=0;
  575.   LPSTR lp;
  576.   
  577.   lstrcpy (szBuf, szMsgBuf);
  578.   strupr (szBuf);
  579.   if ((lp=strstr (szBuf, " BYTES"))!=NULL)
  580.   {
  581.     *lp-- = '\0';
  582.     while (*lp==' ') lp--;
  583.     while (isdigit (*lp)) lp--;
  584.     if (*lp!='\0') lp++;
  585.     nSiz = atol (lp);
  586.     if (nSiz>0) 
  587.     {
  588.       CreateXferWindow();
  589.       SetTotalBytes (nSiz);
  590.     }
  591.   }
  592. }
  593.  
  594. //*****************************************************************************
  595. //*****************************************************************************
  596. int RetrieveFile (SOCKET ctrl_skt, LPSTR szCMD, LPSTR localfile, char rtype)
  597. {
  598.   int iRetCode;
  599.   int iLength;
  600.  
  601.   iCode=0;
  602.   //*************************************************************
  603.   // if we don't have a valid control socket, can't do anything
  604.   //*************************************************************
  605.   if(ctrl_skt==INVALID_SOCKET) 
  606.   {
  607.     DoAddLine("no ctrl_skt, ignored");
  608.     return(0);
  609.   }
  610.  
  611.   //*************************************************************
  612.   // if we are doing something, don't try to do this
  613.   //*************************************************************
  614.   if(bCmdInProgress) 
  615.   {
  616.     DoAddLine("command in process, ignored");
  617.     return(0);
  618.   }
  619.   
  620.   //*************************************************************
  621.   // if the requested type is not the same as the default type
  622.   //*************************************************************
  623.   if (cType!=rtype) 
  624.   {
  625.     switch (rtype)
  626.     {
  627.       case TYPE_L: command(ctrl_skt,"TYPE L 8"); break;
  628.       default    : command(ctrl_skt,"TYPE %c",rtype);
  629.     }
  630.     cType=rtype;
  631.   }
  632.  
  633.   //*************************************************************
  634.   // create a listener socket, if it is successful
  635.   //*************************************************************
  636.   if ((listen_socket=GetFTPListenSocket (ctrl_skt))!=INVALID_SOCKET) 
  637.   {
  638.     //*************************************************************
  639.     // send command to see the result of this all
  640.     // read the control channel (should return 1xx if it worked)
  641.     //*************************************************************
  642.     iRetCode = command ((SOCKET)ctrl_skt,szCMD);
  643.     if (iRetCode==FTP_PRELIM) 
  644.     {
  645.       // wait for connection back to us on the listen socket
  646.       ExtractFileSize();
  647.       iLength = sizeof (saSockAddr1);
  648.       nTimerID = SetTimer (hWndMain, 10, uiTimeOut, NULL);
  649.  
  650.       // get our data connection
  651.       data_socket = accept (listen_socket, (struct sockaddr far *) &saSockAddr1,
  652.                          (int far *)&iLength);
  653.       // turn off the timeout timer
  654.       KillTimer (hWndMain, 10);
  655.       nTimerID = -1;
  656.       // if it failed, we have to quit this
  657.       if (data_socket==INVALID_SOCKET) 
  658.       {
  659.         ReportWSError ("accept",WSAGetLastError());
  660.         listen_socket = DoClose (listen_socket);
  661.         iRetCode=0;
  662.       } 
  663.       else 
  664.       {
  665.         // we don't need the listener socket anymore
  666.         listen_socket = DoClose (listen_socket);
  667.         // inform user of the connection
  668.         DoPrintf ("[%u] accept from %s port %u", data_socket,
  669.           inet_ntoa (saSockAddr1.sin_addr), ntohs (saSockAddr1.sin_port));
  670.         // copy the file
  671.         iRetCode = ReadMass (data_socket, localfile, rtype==TYPE_I);
  672.         // shut the data socket down
  673.         if (iRetCode!=FTP_ABORT)
  674.         {
  675.  
  676.           //************************************************
  677.           // close the data socket
  678.           // read the close control message (should return 2xx)
  679.           //************************************************
  680.           if (shutdown (data_socket, 2)!=0) ReportWSError ("ShutDown", WSAGetLastError());
  681.           data_socket = DoClose (data_socket);
  682.           iRetCode = ReadDisplayLine (ctrl_skt);
  683.         }
  684.         else
  685.         {
  686.           //************************************************
  687.           //  Transfer was aborted, try to shut down gracefully
  688.           //************************************************
  689.           // ForcePacket (ctrl_skt, "ABOR");
  690.           shutdown (data_socket, 2);
  691.           data_socket = DoClose (data_socket);
  692.         }
  693.       }
  694.     } 
  695.     else 
  696.     {
  697.       listen_socket = DoClose (listen_socket);
  698.       iRetCode=0;
  699.       if (bBell) MessageBeep (MB_ICONEXCLAMATION);
  700.     }
  701.  
  702.   } 
  703.   else 
  704.   {
  705.     listen_socket = DoClose (listen_socket);
  706.     iRetCode=0;
  707.     if (bBell) MessageBeep (MB_ICONEXCLAMATION);
  708.   }
  709.   return iRetCode;
  710. }
  711.  
  712. //***********************************************************************
  713. // user close routine
  714. //***********************************************************************
  715. SOCKET DoClose(SOCKET sockfd)
  716. {
  717.   LINGER linger;
  718.  
  719.   if (sockfd!=INVALID_SOCKET) 
  720.   {
  721.     if (WSAIsBlocking()) 
  722.     {
  723.       DoPrintf ("[%u] Cancelled blocking call", sockfd);
  724.       WSACancelBlockingCall();
  725.       bAborted=TRUE;
  726.     }
  727.  
  728.     linger.l_onoff  = TRUE;
  729.     linger.l_linger = 0;
  730.     
  731.     // Patch to make Lanera Stack work
  732.     //setsockopt (sockfd, SOL_SOCKET, SO_LINGER, (LPSTR)&linger, sizeof (linger) );
  733.     if (closesocket (sockfd)==SOCKET_ERROR)
  734.       ReportWSError("CloseSocket", WSAGetLastError());
  735.     else 
  736.     {
  737.       DoPrintf("[%u] Socket closed.",sockfd);
  738.       sockfd=INVALID_SOCKET;
  739.     }
  740.   }
  741.  
  742.   if (sockfd!=INVALID_SOCKET)
  743.     DoPrintf("[%u] Failed to close socket.", sockfd);
  744.  
  745.   return (sockfd);
  746. }
  747.  
  748. //***********************************************************************
  749. //***********************************************************************
  750. int SendPacket (SOCKET sockfd,LPSTR msg)
  751. {
  752.   int i, nRetCode;
  753.  
  754.   if (sockfd==INVALID_SOCKET) return -1;
  755.   if ((bCmdInProgress)&&(lstrcmpi (msg, "quit")!=0))
  756.   {
  757.     DoAddLine ("Command already in progress, ignored New Cmd");
  758.     return -1;
  759.   }
  760.   bCmdInProgress++;
  761.   i=lstrlen (msg);
  762.   lstrcpy (szSendPkt, msg);
  763.   // append a CRLF to the end of outgoing messages
  764.   szSendPkt[i++]='\r';
  765.   szSendPkt[i++]='\n';
  766.   szSendPkt[i]=0;  
  767.   i = sendstr (sockfd, szSendPkt, i, &nRetCode);
  768.   bCmdInProgress--;
  769.   return i;
  770. }
  771.  
  772. //***********************************************************************
  773. //***********************************************************************
  774. int ForcePacket (SOCKET sockfd, LPSTR msg)
  775. {
  776.   int i, nRetCode;
  777.  
  778.   if (sockfd==INVALID_SOCKET) return -1;
  779.  
  780.   bCmdInProgress++;
  781.   i=lstrlen (msg);
  782.   lstrcpy (szSendPkt, msg);
  783.   
  784.   // append a CRLF to the end of outgoing messages
  785.   if (szSendPkt[i-1]!='\n') lstrcat (szSendPkt, "\r\n"), i += 2;  
  786.  
  787.   i = sendstr (sockfd, szSendPkt, i, &nRetCode);
  788.   bCmdInProgress--;
  789.   return i;
  790. }
  791.  
  792. int iMultiLine=0;
  793. //***********************************************************************
  794. // read a reply (may be multi line) and display in debug window
  795. //***********************************************************************
  796. int ReadDisplayLine (SOCKET sockfd)
  797. {
  798.   int iRetCode;
  799.   int iContinue;
  800.   char *s;
  801.   char c;
  802.  
  803.   // can't do anything if we don't have a socket
  804.   if(sockfd==INVALID_SOCKET) return(0);
  805.   
  806.   // let other routine know that we are doing something right now.
  807.   // count the lines in the response
  808.   bCmdInProgress++;
  809.   iMultiLine++;
  810.  
  811.   // initialize some variables, go read the line
  812.   do
  813.   {
  814.     iContinue=0;
  815.     iRetCode = ReadLine (sockfd);
  816.  
  817.     //switch (iRetCode)
  818.     //{
  819.     //  case 257: ConvertSourceDir
  820.     //}
  821.     // if it wasn't a valid value or the 4th char was a hyphen
  822.     // then it is a continuation line
  823.     if (iRetCode<100 || iRetCode>599 || szMsgBuf[3]=='-') iContinue=1;
  824.  
  825.     // send the line we read to our user/debug window
  826.     DoAddLine (szMsgBuf);
  827.  
  828.     // if the timer killed it
  829.     if (bAborted) { iCode=iRetCode=421; iContinue=0; break; }
  830.   
  831.     // we only want to set the real return code in certain situations
  832.     if ((iMultiLine==1 || iCode==0) && iRetCode>99 && iRetCode<600)
  833.        iCode = iRetCode;
  834.     // allow for continuation lines
  835.   }
  836.   while ((iContinue==1) || (iCode>0 && iMultiLine>1 && iRetCode!=iCode));
  837.   // ReadDisplayLine (sockfd);    
  838.   
  839.   // count back down our multiline reponses
  840.   iMultiLine--;
  841.   
  842.   // allow other processes to run
  843.   bCmdInProgress--;
  844.   if (bAborted) return FTP_ABORT;
  845.   
  846.   //************************************************
  847.   // return only the first char of return code
  848.   //************************************************
  849.   if (iCode>99 && iCode<600) return (iCode/100);
  850.   else return 0;
  851.   UNREFERENCED_PARAMETER (c);  
  852.   UNREFERENCED_PARAMETER (s);  
  853. }
  854.  
  855. //***********************************************************************
  856. // read a reply line back in from the sockfd and return the
  857. // value at the beginning of the first line.
  858. //***********************************************************************
  859. int ReadLine (SOCKET sockfd)
  860. {
  861.   LPSTR szBuf;
  862.   int nI;
  863.   int iNumBytes,iN1,iN2,iN3;
  864.   int iBytesRead;
  865.   int iRetCode;
  866.   int i;
  867.   char *s, szTrim[5] = " \r\n";
  868.   char c;
  869.  
  870.   // can't do anything if we don't have a socket
  871.   if (sockfd==INVALID_SOCKET) return(0);
  872.   // let other routines know that we are doing something right now.
  873.   bCmdInProgress++;
  874.   // make sure we don't mistakenly think we timed out
  875.   KillTimer (hWndMain, 10); 
  876.   // bAborted=FALSE;
  877.   nTimerID = -1;
  878.   // zero our receive buffer
  879.   memset (szMsgBuf, 0, 4096);
  880.  
  881.   // initialize some variables
  882.   szBuf=szMsgBuf; iBytesRead=0; iRetCode=0;
  883.  
  884.   // set our timeout
  885.   nTimerID = SetTimer (hWndMain, 10, uiTimeOut, NULL);
  886.   
  887.   // this routine is a little better as it read 80 characters at a time
  888.   // (if it works:-)  Here we PEEK at what is available, find the LF etc...
  889.   iNumBytes=recv (sockfd, (LPSTR) szBuf, 82, MSG_PEEK);
  890.   while (iBytesRead<4000 && (iNumBytes>0))
  891.   {
  892.     // Trumpet WinSock Alpha 15 always returns the len (82) from a recv
  893.     // with MSG_PEEK.  I suppose this is an error??? The spec doesn't say
  894.     // that MSG_PEEK returns something different than normal.
  895.     
  896.     KillTimer (hWndMain, 10);
  897.     nTimerID = -1;
  898.     iN1 = iNumBytes;
  899.  
  900.     // must terminate the string so strchr doesn't go wild.
  901.     szBuf[iNumBytes]='\0';
  902.     
  903.     // find a LF in the input if it exists
  904.     for (nI=0; nI<iNumBytes; nI++) 
  905.     {
  906.       if (szBuf[nI]==0 || szBuf[nI]==0x0a || (bIs5000 && (szBuf[nI]==0x0d))) 
  907.       {
  908.         iNumBytes=nI+1;
  909.         break;
  910.       }
  911.     }
  912.     iN2=iNumBytes;
  913.     // otherwise read up to the full length of what the first recv saw.
  914.     // Wonder what happens here if the second receive actually returns more
  915.     // characters than the first receive and there was a LF in the extra data?   
  916.     iNumBytes = recv (sockfd, (LPSTR) szBuf, iNumBytes, 0);
  917.  
  918.     // again, terminate the string
  919.     szBuf[iNumBytes]=0;
  920.     DoPrintf ("[%u] readline %u - %u - %u %s", sockfd, iN1, iN2, iNumBytes, szBuf);
  921.  
  922.     // bump the receive buffer pointer
  923.     szBuf+=iNumBytes;
  924.  
  925.     // count the bytes that we have read so far
  926.     iBytesRead+=iNumBytes;
  927.  
  928.     // if the last character read was a LF, then stop.  NOTE: this is not really
  929.     // in keeping with RFC 959 as the line MUST end with CRLF but I work with
  930.     // a system (UNISYS 5000) that only returns a LF and no CR at the end of lines.
  931.  
  932.     if(*(szBuf-1)==0x0a || (bIs5000 && *(szBuf-1)==0x0d)) break;
  933.     // otherwise reset the timer and go read more characters
  934.     nTimerID = SetTimer (hWndMain, 10, uiTimeOut, NULL);
  935.     iNumBytes=recv (sockfd, (LPSTR) szBuf, 82, MSG_PEEK);
  936.   }
  937.  
  938.   // if we are here, we have a line or an error or there was nothing to read
  939.   // in any case terminate what we have
  940.   KillTimer(hWndMain, 10);
  941.   nTimerID = -1;
  942.   *szBuf=0;
  943.  
  944.   //*******************************************************
  945.   // find the retcode at the beginning of the line
  946.   //*******************************************************
  947.   if (iNumBytes!=SOCKET_ERROR)
  948.   {
  949.     c=szMsgBuf[3]; 
  950.     szMsgBuf[3]=0;
  951.     iRetCode = atoi (szMsgBuf); 
  952.     szMsgBuf[3]=c;
  953.   }
  954.   else
  955.   {
  956.     int nError = WSAGetLastError();
  957.     
  958.     switch (nError)
  959.     {
  960.       case WSAEINTR    : 
  961.       case WSAENETRESET:
  962.       case WSAESHUTDOWN:
  963.       case WSAENETDOWN :
  964.       case WSAECONNABORTED:
  965.       case WSAECONNRESET:
  966.       case WSAEINVAL   :
  967.       case WSAENOTSOCK : bAborted=TRUE; break;
  968.     }
  969.   }
  970.  
  971.   //*******************************************************
  972.   // if the timer killed it or was somehow aborted
  973.   //*******************************************************
  974.   if (bAborted) iRetCode=421;
  975.  
  976.   // strip trailing blanks, CR's and LF's
  977.   i = lstrlen (szMsgBuf) - 1;
  978.   while ((i>1) && (strchr (szTrim, szMsgBuf[i])!=NULL)) szMsgBuf[i--]=0, i;
  979.   
  980.   // unmark our progress
  981.   bCmdInProgress--;
  982.  
  983.   return iRetCode;  
  984.   UNREFERENCED_PARAMETER (iN3);  
  985.   UNREFERENCED_PARAMETER (s);  
  986. }
  987.  
  988. //***********************************************************************
  989. // based on WINTEL (ftp.c) and BSD (ftp.c)
  990. //***********************************************************************
  991. SOCKET GetFTPListenSocket(SOCKET ctrl_skt)
  992. {
  993.     SOCKET listen_skt;
  994.     int iLength;
  995.     int iRetCode;
  996.     char *a,*p;
  997.     int iFlag=1;
  998.  
  999.     // create a data socket
  1000.     if ((listen_skt=socket (AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET)
  1001.     {
  1002.         ReportWSError ("socket create", WSAGetLastError());
  1003.         return (INVALID_SOCKET);
  1004.     }
  1005.     // let system pick an unused port. we tell remote end with PORT cmd
  1006.     DoPrintf("[%u] going to listen %s port %u",listen_skt,
  1007.       inet_ntoa(saCtrlAddr.sin_addr),ntohs(saCtrlAddr.sin_port));
  1008.  
  1009.     if(bSendPort) 
  1010.     {
  1011.       saCtrlAddr.sin_port=htons(0);
  1012.       saCtrlAddr.sin_family=AF_INET;
  1013.       saCtrlAddr.sin_addr.s_addr=0;
  1014.     } 
  1015.     else
  1016.     {
  1017.       // otherwise we attempt to reuse our ctrl_skt
  1018.       if(setsockopt (listen_skt,SOL_SOCKET,SO_REUSEADDR,
  1019.          (char *)&iFlag, sizeof(iFlag))==SOCKET_ERROR)
  1020.       {
  1021.         ReportWSError ("setsockopt",WSAGetLastError());
  1022.         closesocket (listen_skt);
  1023.         return (INVALID_SOCKET);
  1024.       }
  1025.     }
  1026.     //  Bind name to socket
  1027.     if ( bind((SOCKET)listen_skt,(LPSOCKADDR)&saCtrlAddr,
  1028.              (int)sizeof(struct sockaddr))==SOCKET_ERROR)
  1029.     {
  1030.         ReportWSError("bind",WSAGetLastError());
  1031.         closesocket(listen_skt);
  1032.         return (INVALID_SOCKET);
  1033.     }
  1034.     // get the port name that we got for later transmission in PORT cmd
  1035.     iLength = sizeof (saCtrlAddr);
  1036.     if (getsockname (listen_skt, (struct sockaddr *) &saCtrlAddr, &iLength)<0)
  1037.     {
  1038.       ReportWSError ("getsockname", WSAGetLastError());
  1039.       closesocket (listen_skt);
  1040.       return (INVALID_SOCKET);
  1041.     }
  1042.     // invoke listener
  1043.     if (listen (listen_skt,1)!=0)
  1044.     {
  1045.       ReportWSError ("listen", WSAGetLastError());
  1046.        closesocket (listen_skt);
  1047.       return (INVALID_SOCKET);
  1048.     }
  1049.  
  1050. // inform remote end about our port that we created.
  1051.     if(bSendPort)
  1052.     {
  1053.       struct sockaddr_in saTmpAddr;
  1054.       int iLength;
  1055.  
  1056.       iLength = sizeof (saTmpAddr);
  1057.       if (getsockname(ctrl_skt, (LPSOCKADDR) &saTmpAddr, &iLength)==SOCKET_ERROR)
  1058.       {
  1059.         ReportWSError("getsockname", WSAGetLastError());
  1060.       }
  1061.  
  1062.       a = (char *) &saTmpAddr.sin_addr;
  1063.       p = (char *) &saCtrlAddr.sin_port;
  1064.  
  1065. #define  UC(b)  (((int)b)&0xff)
  1066.       if ((iRetCode=command(ctrl_skt,"PORT %d,%d,%d,%d,%d,%d",
  1067.             UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
  1068.             UC(p[0]), UC(p[1])))!=FTP_COMPLETE) 
  1069.       {
  1070.         DoPrintf("[%u] remote end didn't understand our port command.",listen_skt);
  1071.         return(listen_skt);
  1072.       }
  1073.     }
  1074.     DoPrintf("[%u] listener %s port %u",listen_skt,
  1075.       inet_ntoa (saCtrlAddr.sin_addr), ntohs (saCtrlAddr.sin_port));
  1076.     return (listen_skt);
  1077. }
  1078.  
  1079. #ifdef WIN32
  1080. #define   OPENFIL(x) _lopen (x, OF_READ | OF_SHARE_DENY_NONE)   
  1081. #else
  1082. #define   OPENFIL(x) _lopen (x, READ)
  1083. #endif
  1084.  
  1085. //***********************************************************************
  1086. // Return the size of the specified file
  1087. //***********************************************************************
  1088. LONG GetFileLength (LPSTR lpName, int iFileHandle)
  1089. {
  1090. #ifdef WIN32
  1091.   WIN32_FIND_DATA wfDat;
  1092.   HANDLE hFile;
  1093.   LONG lSize;
  1094.   
  1095.   hFile=FindFirstFile (lpName, &wfDat);
  1096.   if (hFile==INVALID_HANDLE_VALUE) return (LONG) -1;
  1097.   FindClose (hFile);
  1098.   lSize = (wfDat.nFileSizeHigh << 32) + wfDat.nFileSizeLow;
  1099.   return lSize;
  1100. #else
  1101.   return  _filelength (iFileHandle);
  1102. #endif
  1103. }
  1104.  
  1105. //***********************************************************************
  1106. // send a file through the data socket
  1107. //***********************************************************************
  1108. int SendMass (SOCKET sockfd, LPSTR szFileName, BOOL binaryflag)
  1109. {
  1110.   int iNumBytes;
  1111.   int  iRetCode;
  1112.   int  iFileHandle;
  1113.   long lBytesWritten, lBytesToGo;
  1114.   time_t ttStart;
  1115.   time_t ttStop;
  1116.  
  1117.   // if we don't have a socket, return an error  
  1118.   if (sockfd==INVALID_SOCKET || !(bConnected)) return FTP_ERROR;
  1119.   
  1120.   // turn on a flag so other routines know we have a command in progress
  1121.   // initialize some vars
  1122.   bCmdInProgress++;
  1123.   lBytesWritten=0l; iRetCode=FTP_ERROR; 
  1124.  
  1125.   // Open the local file for transmit.
  1126.   if ((iFileHandle=OPENFIL (szFileName))== -1)
  1127.   {
  1128.     DoPrintf ("Could not open file %s (%u)", szFileName, errno);
  1129.     if (bBell) MessageBeep (MB_ICONEXCLAMATION);
  1130.   }
  1131.   else
  1132.   {
  1133.     // get the start time
  1134.     ttStart=time(NULL);
  1135.     iRetCode = FTP_COMPLETE;
  1136.     lBytesToGo=GetFileLength (szFileName, iFileHandle);
  1137.     SetTotalBytes (lBytesToGo);
  1138.     DoPrintf ("Transferring %ld bytes", (long) lBytesToGo);
  1139.     SetXferWindowText (szFileName); 
  1140.     // loop to send output to remote end
  1141.     
  1142.     
  1143.     while (((iNumBytes=_lread (iFileHandle, szMsgBuf, 512))>0)
  1144.            &&(iRetCode!=FTP_ERROR)&&(iRetCode!=FTP_ABORT)&&(!bAborted))
  1145.     {
  1146.       // count the characters that we have sent out
  1147.       lBytesWritten += (LONG) sendstr (sockfd, szMsgBuf, iNumBytes, &iRetCode);
  1148.       wsprintf (szString, "%lu", lBytesWritten);
  1149.       SendMessage (hTxtLBytes, WM_SETTEXT, 0, (LPARAM)(LPCSTR) szString);
  1150.       SetXmitBytes (lBytesWritten);
  1151.     }
  1152.     
  1153.     // if the output file is open, close it
  1154.     _lclose (iFileHandle);
  1155.     switch (iRetCode)
  1156.     {
  1157.       case FTP_ERROR: DoPrintf ("Error on Transfer, aborted"); break;
  1158.       case FTP_ABORT: DoPrintf ("Transfer aborted by User"); break;
  1159.       default       : // show the user how we did
  1160.                       // get the finish time
  1161.                       ttStop=time(NULL);
  1162.                       SendMessage (hTxtLBytes, WM_SETTEXT, 0, (LPARAM) NULL);
  1163.                       PrintTransferStatus ("Transmitt", lBytesWritten, (LONG) ttStop-ttStart);
  1164.                       iRetCode=FTP_COMPLETE;
  1165.                       if (bBell) MessageBeep(MB_OK);
  1166.     }
  1167.   }
  1168.   // turn off our command in progress flag
  1169.   bCmdInProgress--;
  1170.  
  1171.   return (iRetCode);
  1172. }
  1173.  
  1174. //***********************************************************************
  1175. // read information from the data socket into a file.  
  1176. //***********************************************************************
  1177. int ReadMass (SOCKET sockfd, LPSTR szFileName,BOOL binaryflag)
  1178. {
  1179.   int  iNumBytes;
  1180.   int  iRetCode;
  1181.   int  iFileHandle;
  1182.   long lBytesRead;
  1183.   time_t ttStart;
  1184.   time_t ttStop;
  1185.  
  1186.   // if we don't have a socket, return an error  
  1187.   // otherwise turn on a flag so other routines know about cmd in progress
  1188.   if(sockfd==INVALID_SOCKET || !(bConnected)) return 0;
  1189.   bCmdInProgress++;
  1190.  
  1191.   //************************************************
  1192.   // make sure we don't mistakenly think we timed out
  1193.   // initialize some vars
  1194.   //************************************************
  1195.   KillTimer (hWndMain, 10); bAborted=FALSE;
  1196.   nTimerID = -1;
  1197.   lBytesRead=0l; iRetCode=0; 
  1198.  
  1199.   //***********************************************************************
  1200.   // at the moment we are ignoring the fact that the local destination file
  1201.   // may not open correctly.
  1202.   //***********************************************************************
  1203.   if ((iFileHandle=_lcreat (szFileName,0))== -1)
  1204.   {
  1205.     DoPrintf ("Failed to create file %s (%u)", szFileName,errno);
  1206.     bCmdInProgress--;
  1207.     return FTP_ERROR;
  1208.   }
  1209.  
  1210.   //************************************************
  1211.   // get the start time
  1212.   //************************************************
  1213.   SetXferWindowText (szFileName); 
  1214.   ttStart = time (NULL);
  1215.   
  1216.   // loop to receive input from remote end
  1217.   iNumBytes = recv (sockfd, (LPSTR)szMsgBuf, 4000, 0);
  1218.   while (!bAborted && (iNumBytes>0) && (iNumBytes!=SOCKET_ERROR))
  1219.   {
  1220.     //************************************************
  1221.     // write what we received if the file is open
  1222.     // and count the characters that we received
  1223.     //************************************************
  1224.     _lwrite (iFileHandle, szMsgBuf, iNumBytes);
  1225.     lBytesRead += iNumBytes;
  1226.     SetXmitBytes (lBytesRead);
  1227.     wsprintf (szString, "%lu", lBytesRead);
  1228.     SendMessage (hTxtRBytes, WM_SETTEXT, 0, (LPARAM)(LPCSTR) szString);
  1229.     if (!bAborted) iNumBytes = recv (sockfd, (LPSTR) szMsgBuf, 4000, 0);
  1230.   }
  1231.   
  1232.   //************************************************
  1233.   // get the finish time
  1234.   // if the output file is open, close it
  1235.   //************************************************
  1236.   ttStop = time (NULL);
  1237.   if (iFileHandle != -1)  _lclose (iFileHandle);
  1238.   if (bAborted) _unlink (szFileName), DoPrintf ("Delete partially downloaded file"); 
  1239.  
  1240.   //************************************************
  1241.   // if we had a recv error, let us know about it
  1242.   //************************************************
  1243.   if (iNumBytes==SOCKET_ERROR)
  1244.   {
  1245.     ReportWSError ("Recv", iRetCode=WSAGetLastError());
  1246.     if (lBytesRead==0l)
  1247.     {
  1248.       if(bBell) MessageBeep (MB_ICONEXCLAMATION);
  1249.     }
  1250.   }
  1251.   else
  1252.   {
  1253.     //************************************************
  1254.     // show the user how we did
  1255.     // turn off our command in progress flag
  1256.     //************************************************
  1257.     SendMessage (hTxtRBytes, WM_SETTEXT, 0, (LPARAM) NULL);
  1258.     PrintTransferStatus ("Receiv", lBytesRead, (LONG) ttStop-ttStart);
  1259.     if (bBell) MessageBeep (MB_OK);
  1260.     iRetCode = (bAborted) ? FTP_ABORT : FTP_COMPLETE;
  1261.   }
  1262.   bCmdInProgress--;
  1263.   return iRetCode;
  1264. }
  1265.  
  1266.